package gang.org.springframework.framework.support;

import gang.org.springframework.framework.core.GangKotlinDetector;

import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * @author gang.chen
 * @since  2021/11/10 13:11
 */
public class GangMethodParameter {
    private final Executable executable;
    private final int parameterIndex;
    private int nestingLevel;
    private volatile Class<?> containingClass;
    private volatile Type genericParameterType;

    public volatile Class<?> parameterType;


    /**
     * 业务意义，初始化MethodParameter对象
     * @param executable Method对象
     * @param parameterIndex value -1
     * @param nestingLevel value 1
     * */
    public GangMethodParameter(Executable executable, int parameterIndex, int nestingLevel) {
        this.executable = executable;
        this.parameterIndex = parameterIndex;
        this.nestingLevel = nestingLevel;
    }

    public GangMethodParameter(Method method, int parameterIndex){
        this(method, parameterIndex, 1);
    }

    public GangMethodParameter(Constructor<?> constructor, int parameterIndex){
        this(constructor, parameterIndex, 1);
    }

    public int getNestingLevel() {
        return this.nestingLevel;
    }

    public Class<?> getContainingClass() {
        Class<?> containingClass = this.containingClass;
        return containingClass != null? containingClass: getDeclaringClass();
    }

    /**
     * 业务意义
     * this.executable.getDeclaringClass 返回声明该构造方法的类或接口
     * */
    public Class<?> getDeclaringClass(){
        return this.executable.getDeclaringClass();
    }

    /**
     * 业务意义
     * executable 接收Method -> mvcResourceUrlProvider
     * */
    public Method getMethod(){
        return (this.executable instanceof Method ? (Method)this.executable : null);
    }

    public int getParameterIndex() {
        return this.parameterIndex;
    }

    /**
     * 业务意义
     * 获取方法的返回对象
     * */
    public Type getGenericParameterType() {
        Type paramType = this.genericParameterType;

        if (paramType == null) {
            /**
             * 1）场景一
             *      IF场景成立，当前业务
             * 2）场景二
             *      ELSE暂时不做考虑
             * */
            if (this.parameterIndex < 0) {
                //mvcResourceUrlProvider是这个方法
                Method method = getMethod();
                //暂时不做考虑
                //boolean kotlinReflectPresent = GangKotlinDetector.isKotlinReflectPresent();
                //boolean kotlinType = GangKotlinDetector.isKotlinType(getContainingClass());
                //paramType = (method !=null ? (kotlinReflectPresent & kotlinType ? GangKotlinDetector.getGenericReturnType(method):method.getGenericReturnType()) : void.class);
                paramType = method.getGenericReturnType();
            }
            else {
                //TODO
            }
            this.genericParameterType = paramType;
        }
        return paramType;
    }

    public static GangMethodParameter forExecutable(Executable executable, int parameterIndex){

        if (executable instanceof Method) {
            return new GangMethodParameter((Method) executable, parameterIndex);
        }
        else if (executable instanceof Constructor){
            return new GangMethodParameter((Constructor<?>) executable, parameterIndex);
        }
        else {
            return null;
        }
    }

    public static int validateIndex(Executable executable, int parameterIndex){
        int parameterCount = executable.getParameterCount();
        return parameterIndex;
    }

    public Class<?> getParameterType(){
        Class<?> paramType = this.parameterType;
        if (paramType != null) {
            return paramType;
        }
        if (false){
            //TODO
        }
        if (paramType == null) {
            paramType = computeParameterType();
        }

        return paramType;
    }

    public Executable getExecutable() {
        return executable;
    }

    public Class<?> getNestedParameterType(){
        if (this.nestingLevel > 1) {
            //TODO
            return null;
        }else {
            return getParameterType();
        }
    }

    public Class<?> computeParameterType(){
        if (false){
            //TODO
        }
        return this.executable.getParameterTypes()[parameterIndex];
    }
}
